#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/8/1 20:58
# @Author  : huidong.bai
# @File    : common.py
# @Software: PyCharm
# @Mail    : MasterBai2018@outlook.com
import time
import datetime
import socket
import psutil
import shutil
import os
import pickle

import pytest
import yaml
import configparser


color_template = {"red": "\033[1;31m%s\033[0m",
                  "green": "\033[1;32m%s\033[0m",
                  "yellow": "\033[1;33m%s\033[0m",
                  "blue": "\033[1;34m%s\033[0m",
                  "cyan": "\033[36m%s\033[0m",
                  "None": "%s"}


def method(prototype):
    class MethodDescriptor(object):
        def __init__(self, func):
            self.func = func
            self.bound_funcs = {}

        def __get__(self, obj, types=None):
            assert obj is not None
            try:
                return self.bound_funcs[obj, types]
            except KeyError:
                rets = self.bound_funcs[obj, types] = prototype(self.func.__get__(obj, types))
                return rets
    return MethodDescriptor


def async_color_print(*args):
    color_msg = ""
    for msg, color in args:
        color_msg += color_template[str(color)] % msg
    print(color_msg)


def color_print(*args):
    color_msg = ""
    for msg, color in args:
        color_msg += color_template[str(color)] % msg
    print(color_msg)

def get_log_time():
    timestamp = datetime.datetime.now()
    formatted_time = timestamp.strftime("%Y-%m-%d %H:%M:%S.%f")
    return formatted_time[:-3]



def get_timestamp():
    return int(time.time() * 1000)


def find_free_ports(num_ports=2, start_port=8000):
    ports = []
    current_port = start_port

    while len(ports) < num_ports:
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.bind(('0.0.0.0', current_port))
                ports.append(current_port)
        except socket.error:
            pass
        current_port += 1
    return ports


def kill_process_by_port(port) -> int:
    for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
        try:
            connections = proc.connections()
            for conn in connections:
                if conn.status == psutil.CONN_LISTEN and conn.laddr.port == port:
                    try:
                        proc.terminate()
                        return 0
                    except psutil.NoSuchProcess:
                        return -1
        except psutil.AccessDenied:
            pass
    return -1


def profile_redirect(source, destination, root, config_items) -> int:
    if (not os.path.exists(source)) or os.path.getsize(source) == 0:
        return -1
    new_file = open(destination, 'w+', encoding='UTF-8')
    try:
        with open(source, 'r', encoding='UTF-8') as fp:
            lines = fp.readlines()
            for line in lines:
                info = line.strip()
                if line.isspace() or info[0] == "#" or info[:2] == "//":
                    continue
                for key, value in config_items.items():
                    if key in line:
                        if type(value) == int:
                            new_value = f'{key} = {value};\n'
                        elif type(value) == bool:
                            new_value = f'{key} = {str(value).lower()};\n'
                        else:
                            new_value = f'{key} = "{value}";\n'
                        new_file.write(new_value)
                        del config_items[key]
                        break
                else:
                    new_file.write(line)

            for key, value in config_items.items():
                if type(value) == int:
                    new_value = f'{key} = {value};\n'
                elif type(value) == bool:
                    new_value = f'{key} = {str(value).lower()};\n'
                else:
                    new_value = f'{key} = "{value}";\n'
                new_file.write(new_value)
        fp.close()

        new_file.close()
        return 0
    except Exception as e:
        new_file.close()
        print(f"rewrite file[{source}] error: {e}")
        return -1


def replace_file(file, old, new) -> int:
    if not os.path.exists(file):
        print(f"Replace file error, file: [{file}], pls check it.")
        return -1
    if (not old) or (not new):
        print(f"Replace file error, file: [{file}], old: [{old}], new: [{new}], pls check it.")
        return -1
    cmd = f"sed -i 's|{old}|{new}|g' {file}"
    os.system(cmd)
    return file


def copy_configs(source_path, dest_dir, old, new):
    if old is None or new is None or source_path is None or dest_dir is None:
        pytest.exit(reason=f"Copy config file [{source_path}] error, old value is {old}, new value is {new}.")
    config_name = os.path.basename(source_path)
    dest_path = os.path.join(dest_dir, config_name)
    sed_cmd = f"sed 's|{old}|{new}|g' {source_path} > {dest_path}"
    os.system(sed_cmd)
    return dest_path


def copy_libs(item, key, destination, project):
    project_config = configparser.ConfigParser()
    project_config.read('conf/project.ini')
    mango_config = configparser.ConfigParser()
    mango_config.read('conf/mongo.ini')
    lib_name = mango_config.get(item, key).split('/')[-1]
    local_lib_path = os.path.join(project_config.get(f'{project}', 'path'), lib_name)
    if not os.path.exists(local_lib_path):
        pytest.exit(reason=f"Copy lib path error: {local_lib_path}")

    shutil.copy(local_lib_path, destination)
    return os.path.join(destination, lib_name)


def is_available_file(file: str):
    if not file:
        return False
    else:
        if os.path.exists(file) and os.path.getsize(file) > 0 and os.path.isfile(file):
            return True
        else:
            return False


def read_config(path):
    with open(path, "rb") as fp:
        serialized_object = fp.read()
        fp.close()
    return pickle.loads(serialized_object)


def uuid(tag=None):
    import uuid
    uuids = str(uuid.uuid4()).upper().replace("-", '')
    if tag:
        return uuids + str(tag)
    else:
        return uuids


def load_yaml_config(yaml_config):
    try:
        with open(yaml_config, 'r') as file:
            _config = yaml.safe_load(file)
        file.close()
        return _config
    except yaml.YAMLError as e:
        print(f"Error loading yaml file: {e}")
        raise e


def get_open_file_count_psutil():
    try:
        # 获取当前进程对象
        process = psutil.Process()
        # 获取当前进程已打开的文件句柄数量
        return process.num_fds()
    except psutil.Error as e:
        print(f"Error: {e}")
        return None


def trans_digit(param: str):
    try:
        num = float(param)
        return int(num) if num.is_integer() else num
    except ValueError:
        return None


def pop(items: list, index: int):
    try:
        result = items.pop(index)
    except IndexError:
        result = ""
    return result


def is_english(s):
    start_is_eng = False
    end_is_eng = False
    if len(s) > 0:
        start_char = s[0]
        end_char = s[-1]
        if (65 <= ord(start_char) <= 90 or 97 <= ord(start_char) <= 122):
            start_is_eng = True
        if (65 <= ord(end_char) <= 90 or 97 <= ord(end_char) <= 122):
            end_is_eng = True
        return start_is_eng, end_is_eng
    else:
        return start_is_eng, end_is_eng


def join_with_spaces_if_english(text):
    words = text.split("|")
    result = []

    for i in range(len(words) - 1):
        # 检查是否包含英文
        _, end_is_eng = is_english(words[i])
        start_is_eng, _ = is_english(words[i+1])

        if start_is_eng and end_is_eng:
            result.append(words[i] + " ")
        else:
            result.append(words[i])

    result.append(words[-1])
    return ''.join(result)
